package com.hk.core.autoconfigure.web.servlet;

import com.hk.commons.annotations.RepeatSubmit;
import com.hk.commons.cache.ExpireCache;
import com.hk.commons.exceptions.RepeatSubmitException;
import com.hk.commons.util.StringUtils;
import com.hk.core.authentication.api.SecurityContextUtils;
import com.hk.core.web.Webs;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.http.HttpMethod;
import org.springframework.lang.Nullable;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import java.util.Objects;
import java.util.Optional;


/**
 * 表单重复提交
 *
 * @author Kevin
 * @see RepeatSubmit
 */
public record RepeatSubmitHandlerInterceptor(boolean postOnly, ExpireCache cache) implements HandlerInterceptor {

    private static final String REPEAT_SUBMIT_REQUEST_KEY = "RepeatSubmitHandlerRequestKey";

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        var method = request.getMethod();
        if (postOnly && !StringUtils.equalsIgnoreCase(method, HttpMethod.POST.name())) {
            return true;
        }
        if (handler instanceof HandlerMethod handlerMethod) {
            RepeatSubmit repeatSubmit = handlerMethod.getMethodAnnotation(RepeatSubmit.class);
            if (Objects.isNull(repeatSubmit)) {
                return true;
            }
            String requestKey = generatorRequestKey(request, handlerMethod, repeatSubmit);
            Optional<String> value = cache.get(requestKey, String.class);
            if (value.isEmpty()) {
                cache.set(requestKey, "1", repeatSubmit.expire() <= 0 ? -1 : repeatSubmit.expire(), repeatSubmit.unit());
                request.setAttribute(REPEAT_SUBMIT_REQUEST_KEY, requestKey);
                return true;
            }
            throw new RepeatSubmitException("重复提交表单");
        }
        return true;
    }

    private String generatorRequestKey(HttpServletRequest request, HandlerMethod handlerMethod,
                                       RepeatSubmit repeatSubmit) {
        var remoteAddress = Webs.getRemoteAddr(request);
        var method = handlerMethod.getMethod();
        var sb = new StringBuilder();
        sb.append("RSubmit-");
        if (StringUtils.isNotEmpty(remoteAddress)) {
            sb.append(remoteAddress).append('-');
        }
        if (SecurityContextUtils.isAuthenticated()) {
            sb.append(SecurityContextUtils.getPrincipal().getUserId()).append('-');
        }
        String value = repeatSubmit.value();
        if (StringUtils.startsWith(value, "request.")) {
            var parameterName = value.substring(8);
            var parameterValue = StringUtils.hasLength(parameterName) ? request.getParameter(parameterName) : null;
            if (StringUtils.hasLength(parameterValue)) {
                value = parameterValue;
            }
        } else if (StringUtils.startsWith(value, "header.")) {
            var headerName = value.substring(7);
            var headerValue = StringUtils.hasLength(headerName) ? request.getHeader(headerName) : null;
            if (StringUtils.hasLength(headerValue)) {
                value = headerValue;
            }
        }
        return sb.append(method.getDeclaringClass().getSimpleName())
                .append('-')
                .append(method.getName())
                .append('-')
                .append(handlerMethod.getMethodParameters().length)
                .append('-')
                .append(value)
                .toString();
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
                                @Nullable Exception ex) {
        var key = (String) request.getAttribute(REPEAT_SUBMIT_REQUEST_KEY);
        if (StringUtils.hasLength(key)) {
            cache.evict(key);
        }
    }
}
